package com.n3wax.automation.security.ademco.ad2usb;

import com.n3wax.automation.security.AlarmConstants;
import com.n3wax.automation.security.AlarmControl;
import com.n3wax.automation.security.ademco.AlarmEventConsumer;
import com.n3wax.automation.security.ademco.AlarmSystemStatus;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;

public class AD2USBController implements AD2USBEventConsumer, AlarmConstants, AlarmControl, Runnable{

    private boolean zone01fault = false;
    private boolean zone02fault = false;
    private boolean zone03fault = false;
    private boolean zone04fault = false;
    private boolean zone05fault = false;
    private boolean zone06fault = false;
    private boolean zone07fault = false;
    private boolean zone08fault = false;
    private boolean zone09fault = false;
    private boolean zone10fault = false;
    private boolean zone11fault = false;
    private boolean zone12fault = false;
    private boolean zone13fault = false;
    private boolean zone14fault = false;
    private boolean zone15fault = false;
    private boolean zone16fault = false;

    private Date zone01time = new Date();
    private Date zone02time = new Date();
    private Date zone03time = new Date();
    private Date zone04time = new Date();
    private Date zone05time = new Date();
    private Date zone06time = new Date();
    private Date zone07time = new Date();
    private Date zone08time = new Date();
    private Date zone09time = new Date();
    private Date zone10time = new Date();
    private Date zone11time = new Date();
    private Date zone12time = new Date();
    private Date zone13time = new Date();
    private Date zone14time = new Date();
    private Date zone15time = new Date();
    private Date zone16time = new Date();


    private int systemState;

    private boolean newUpdateAvailable = false;

    private AlarmSystemStatus currentStatus = new AlarmSystemStatus();

    private boolean chimeEnabled = false;

    private Thread executorThread;

    AD2USBCommInterface commInt;
    AlarmEventConsumer consumer = null;

    public AD2USBController()
    {
        commInt = new AD2USBCommInterface(this);
        executorThread = new Thread(this);
        executorThread.start();

    }

    public void setAlarmConsumer(AlarmEventConsumer newConsumer)
    {
        consumer = newConsumer;
    }

    public void receiveEvent(String bitmap, String numericMessage, String panelMessage) {
        updateSystemStatus(bitmap);
        updateZoneStatus(numericMessage, panelMessage);
        if(newUpdateAvailable)
        {
            updateStatusObject();
            notifyConsumers();
            newUpdateAvailable = false;
        }

    }

    private void notifyConsumers()
    {
        if(consumer != null)
        {
            consumer.alarmUpdate(currentStatus);
        }
    }

    private void updateZoneStatus(String numericMessage, String panelMessage)
    {
        try {
            String zoneIdentifier = "zone" + numericMessage + "fault";
            String zoneTimestampIdentifier = "zone" + numericMessage + "time";
            Class commInt = Class.forName("com.n3wax.automation.security.ademco.ad2usb.AD2USBController");
            Field zoneField = commInt.getDeclaredField(zoneIdentifier);
            Field zoneTimestamp = commInt.getDeclaredField(zoneTimestampIdentifier);
            zoneField.setAccessible(true);
            zoneTimestamp.setAccessible(true);


            if(panelMessage.indexOf("FAULT") != -1)
            {
                if(zoneField.getBoolean(this) == false)
                {
                    newUpdateAvailable = true;
                }
                zoneField.setBoolean(this, true);
                zoneTimestamp.set(this, new Date());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void updateSystemStatus(String statusString)
    {
        int currentSystemState = systemState;
        if(statusString.substring(0,1).equalsIgnoreCase("0"))
        {
            systemState = _NOT_READY;
        }

        if(statusString.substring(0,1).equalsIgnoreCase("1"))
        {
            systemState = _READY;
        }

        if(statusString.substring(1,2).equalsIgnoreCase("1"))
        {
            systemState = _ARMED_AWAY;
        }

        if(statusString.substring(2, 3).equalsIgnoreCase("1"))
        {
            systemState = _ARMED_STAY;
        }

        if(statusString.substring(2,3).equalsIgnoreCase("1") && statusString.substring(13,14).equalsIgnoreCase("1"))
        {
            systemState = _ARMED_INSTANT;
        }

        if(statusString.substring(9,10).equalsIgnoreCase("1"))
        {
            chimeEnabled = true;
        }
        else
        {
            chimeEnabled = false;
        }

        if(currentSystemState != systemState)
        {
            newUpdateAvailable = true;
        }
    }

    public int getSystemStatus()
    {
        return systemState;
    }

    private void updateStatusObject()
    {
        currentStatus.alarmStatus = this.systemState;
        currentStatus.zone01fault = this.zone01fault;
        currentStatus.zone02fault = this.zone02fault;
        currentStatus.zone03fault = this.zone03fault;
        currentStatus.zone04fault = this.zone04fault;
        currentStatus.zone05fault = this.zone05fault;
        currentStatus.zone06fault = this.zone06fault;
        currentStatus.zone07fault = this.zone07fault;
        currentStatus.zone08fault = this.zone08fault;
        currentStatus.zone09fault = this.zone09fault;
        currentStatus.zone10fault = this.zone10fault;
        currentStatus.zone11fault = this.zone11fault;
        currentStatus.zone12fault = this.zone12fault;
        currentStatus.zone13fault = this.zone13fault;
        currentStatus.zone14fault = this.zone14fault;
        currentStatus.zone15fault = this.zone15fault;
        currentStatus.zone16fault = this.zone16fault;
    }

    public void arm(int mode, String userCode, boolean bypassOpenZones) {



        if(bypassOpenZones)
        {
            autoBypass(userCode);
        }

        String commandString = null;
        if(mode == _ARMED_AWAY)
        {
            commandString = userCode + "2";
        }

        if(mode == _ARMED_INSTANT)
        {
            commandString = userCode + "4";
        }
        if(mode == _ARMED_STAY)
        {
            commandString = userCode + "3";
        }

        commInt.sendCommand(commandString);

    }

    public void disarm(String userCode) {
        commInt.sendCommand(userCode + "1");
    }

    public void setChime(String userCode, boolean chimeOn) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void autoBypass(String userCode)
    {
        Vector <String> openZones = new Vector<String>();
        try {

            Class controllerClass = Class.forName("com.n3wax.automation.security.ademco.ad2usb.AD2USBController");
            for(int cnt = 1; cnt <16; cnt++)
                  {

                  String zoneIdentifier;
                  if(cnt < 10)
                  {

                      zoneIdentifier = "zone0" + cnt + "fault";
                  }
                  else
                  {

                      zoneIdentifier = "zone" + cnt + "fault";
                  }

                  Field zoneFault = controllerClass.getDeclaredField(zoneIdentifier);
                  zoneFault.setAccessible(true);
                  if(zoneFault.getBoolean(this))
                  {
                     if(cnt < 10)
                     {
                      openZones.addElement((String)"0" + cnt);
                     }
                      else
                     {
                         openZones.addElement(String.valueOf(cnt));
                     }
                  }
                  }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        bypass(userCode, openZones);
    }

    public void bypass(String userCode, Vector zones) {

        Iterator it = zones.iterator();
        commInt.sendCommand(userCode + "6");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        while(it.hasNext())
        {
            String zoneCode = (String)it.next();
            System.out.println("zoneCode = " + zoneCode);
            commInt.sendCommand(zoneCode);
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {

            }
        }

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public void panicPolice() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void panicFire() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void panicMedical() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void forceUpdate() {
        notifyConsumers();
    }

    public void run() {
        while(true)
        {
            if(systemState == _NOT_READY)
            {
                commInt.sendCommand("*");
            }
            try {
                Thread.sleep(30000);


                Class commInt = Class.forName("com.n3wax.automation.security.ademco.ad2usb.AD2USBController");

                for(int cnt = 1; cnt <16; cnt++)
                {
                String zoneTimestampIdentifier;
                String zoneIdentifier;
                if(cnt < 10)
                {
                    zoneTimestampIdentifier = "zone0" + cnt + "time";
                    zoneIdentifier = "zone0" + cnt + "fault";
                }
                else
                {
                    zoneTimestampIdentifier = "zone" + cnt + "time";
                    zoneIdentifier = "zone" + cnt + "fault";
                }
                Field zoneTimestamp = commInt.getDeclaredField(zoneTimestampIdentifier);
                Field zoneFault = commInt.getDeclaredField(zoneIdentifier);
                zoneTimestamp.setAccessible(true);
                zoneFault.setAccessible(true);

                if(System.currentTimeMillis() > ((Date)zoneTimestamp.get(this)).getTime() + 30000)
                {
                    if(zoneFault.getBoolean(this) == true)
                    {
                        zoneFault.setBoolean(this, false);
                        newUpdateAvailable = true;
                    }
                }

                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    public AlarmSystemStatus getSystemStatusDetail() {
        return currentStatus;
    }
}
